<!DOCTYPE html>
<html class="no-js" lang="zh">
  <head>
<meta charset="utf-8">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<link rel="alternate" href="https://www.desgard.com" hreflang="pt-BR">
<link href="http://gmpg.org/xfn/11" rel="profile">
<meta name="HandheldFriendly" content="True">
<meta name="MobileOptimized" content="320">
<meta name="theme-color" content="#005344">
<title>Guardia · 瓜地</title>
<meta name="google-site-verification" content="zS1dSn20XtA4FJYEOQLXqI0boxZdMnJ2g3beje-cl20">
<meta name="description" content="I write many code to write less code.💻">
<meta name="keywords" content="">
<!-- Social: Facebook / Open Graph -->
<meta property="og:url" content="https://www.desgard.com/objc_msgSend2/">
<meta property="og:title" content="       objc_msgSend消息传递学习笔记 - 消息转发 | Gua  ">
<meta property="og:description" content="I write many code to write less code.💻">
<meta property="og:site_name" content="Desgard_Duan">
<meta property="og:locale" content="pt_BR">
<meta property="og:type" content="website">
<meta property="og:author" content="https://www.facebook.com/desgard.duan">
<meta property="og:image" content="https://www.desgard.com">
<!-- Social: Twitter -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@nandomoreirame">
<meta name="twitter:domain" content="https://www.desgard.com">
<meta name="twitter:title" content="       objc_msgSend消息传递学习笔记 - 消息转发 | Gua  ">
<meta name="twitter:description" content="I write many code to write less code.💻">
<meta name="twitter:image:src" content="https://www.desgard.com">
<!-- Favicons -->
<link rel="apple-touch-icon" sizes="114x114" href="https://www.desgard.com/assets/ico/apple-touch-icon-114-516f4e19976b9e4dbb77ad9b576831fe.png">
<link rel="apple-touch-icon" sizes="72x72" href="https://www.desgard.com/assets/ico/apple-touch-icon-72-5409b2df229305703caf583d86c845ab.png">
<link rel="apple-touch-icon" href="https://www.desgard.com/assets/ico/apple-touch-icon-57-aa873e019cf659e0d4e6a0b5bb9f379d.png">
<link rel="shortcut icon" href="https://www.desgard.com/assets/ico/favicon-4298be3d3fbe23e18d65bded9d930899.png">
<!-- rel prev and next -->
<!-- Canonical link tag -->
<link rel="canonical" href="https://www.desgard.com/objc_msgSend2/">
<link rel="alternate" type="application/rss+xml" title="Gua" href="https://www.desgard.com/feed.xml">
<link rel="stylesheet" href="/assets/main-0b7b828712f4c43b75bba4861c907bb1.css">
<script src="/assets/modernizr-custom-cb807611a3e262b2eac59444cbab74d6.js" data-cfasync="false"></script>
<script src="/assets/js/jquery-3.1.js"></script>
<script src="/assets/js/jquery.rotate.js"></script>
<script type="text/javascript" src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script type="application/ld+json">
{
  "@context": "http://schema.org",
  "@type": "Website",
  "publisher": "www.desgard.com",
  "url": "http://www.desgard.com/",
  "description": "瓜地"
}
</script>
<script type="text/javascript">
  var disqus_shortname = 'desgard',
      baseurl          = '';
</script>
<!--
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-52446115-1']);
_gaq.push(['_trackPageview']);
(function() {
  var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
  ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
  var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>-->
<script>
var _hmt = _hmt || [];
(function() {
  var hm = document.createElement("script");
  hm.src = "//hm.baidu.com/hm.js?c5a8123bc51782a3083a631ed9ff50e4";
  var s = document.getElementsByTagName("script")[0]; 
  s.parentNode.insertBefore(hm, s);
})();
</script>
<script type="text/x-mathjax-config">
    MathJax.Hub.Config({
      tex2jax: {
        inlineMath: [ ['$','$'] ],
        displayMath: [ ['$$','$$'] ],
        processEscapes: true
      }
    });
  </script>
  </head>
  <body class="post-template single">
    <header class="header">
  <div class="container">
    <h1><a href="/"><strong>desgard</strong>.com</a></h1>
    <nav class="navbar">
      <ul>
        <li><a href="/">Home</a></li>
        <li><a href="#!" data-modal="modalSearch"><i class="fa fa-search"></i></a></li>
        <li><a href="/comment " target="_blank"><i class="fa fa-comments"></i></a></li>
        <li><a href="/feed.xml" target="_blank"><i class="fa fa-feed"></i></a></li>
      </ul>
    </nav>
  </div>
</header>
    <main class="wrapper container" itemprop="mainContentOfPage" itemscope="itemscope" itemtype="http://schema.org/Blog">
      <article class="post" itemscope="itemscope" itemtype="http://schema.org/BlogPosting" itemprop="blogPost">
  <header class="post-header">
    <h1>objc_msgSend消息传递学习笔记 - 消息转发</h1>
    <div class="post-meta">
      <time datetime="2015-10-13">
        <i class="fa fa-calendar-o"></i> <time datetime="2016-08-27"> 2016-08-27
      </time>
      <span>
        <i class="fa fa-comments"></i> <a href="#container">Comment me!</a>
      </span>
      <span>
<!--
        <i class="fa fa-folder-open-o"></i> 
-->
      </span>
    </div>
  </header>
  <p>该文是 <em><a href="https://desgard.com/objc_msgSend1/">objc_msgSend消息传递学习笔记 - 对象方法消息传递流程</a></em> 的基础上继续探究源码，请先阅读上文。</p>
<h2 id="message-forwarding">消息转发机制(message forwarding)</h2>
<p>Objective-C 在调用对象方法的时候，是通过消息传递机制来查询且执行方法。如果想令该类能够理解并执行方法，必须以程序代码实现出对应方法。但是，在编译期间向类发送了无法解读的消息并不会报错，因为在 runtime 时期可以继续向类添加方法，所以编译器在编译时还无法确认类中是否已经实现了消息方法。</p>
<p>当对象接受到无法解读的消息后，就会启动<strong>消息转发</strong>机制，并且我们可以由此过程告诉对象应该如何处理位置消息。</p>
<p>本文的研究目标：当 Class 对象的 <code>.h</code> 文件中声明了成员方法，但是没有对其进行实现，来跟踪一下 runtime 的消息转发过程。于是创造一下实验场景：</p>
<blockquote>
  <p>同上一篇文章一样，定义一个自定义 Class <code>DGObject</code> ，并且声明改 Class 中拥有方法 <code>- (void)test_no_exist</code> ，而在 <code>.m</code> 文件中不给予实现。在 <code>main.m</code> 入口中直接调用该类某实例的 <code>- (void)test_no_exist</code> 方法。</p>
</blockquote>
<p><img src="http://7xwh85.com1.z0.glb.clouddn.com/14722248187304.jpg" alt="" /></p>
<h2 id="section">动态方法解析</h2>
<p>依旧在 lookUpImpOrForward 方法中下断点，并单步调试，观察代码走向。由于方法在方法列表中无法找到，所以立即进入 method resolve 过程。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="sr">//</span> <span class="err">进入</span><span class="nb">method</span> <span class="n">resolve</span><span class="err">过程</span>
<span class="k">if</span> <span class="p">(</span><span class="n">resolver</span>  <span class="o">&amp;&amp;</span>  <span class="o">!</span><span class="n">triedResolver</span><span class="p">)</span> <span class="p">{</span>
	<span class="sr">//</span> <span class="err">释放读入锁</span>
   <span class="n">runtimeLock</span><span class="o">.</span><span class="n">unlockRead</span><span class="p">();</span>
   <span class="sr">//</span> <span class="err">调用</span><span class="n">_class_resolveMethod</span><span class="err">，解析没有实现的方法</span>
   <span class="n">_class_resolveMethod</span><span class="p">(</span><span class="n">cls</span><span class="p">,</span> <span class="n">sel</span><span class="p">,</span> <span class="n">inst</span><span class="p">);</span>
   <span class="sr">//</span> <span class="err">进行二次尝试</span>
   <span class="n">triedResolver</span> <span class="o">=</span> <span class="no">YES</span><span class="p">;</span>
   <span class="n">goto</span> <span class="k">retry</span><span class="p">;</span>
<span class="p">}</span></code></pre></div>
<p><code>runtimeLock.unlockRead()</code> 是释放读入锁操作，这里是指缓存读入，即缓存机制不工作从而不会有缓存结果。随后进入 <code>_class_resolveMethod(cls, sel, inst)</code> 方法。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">void</span> <span class="n">_class_resolveMethod</span><span class="p">(</span><span class="no">Class</span> <span class="n">cls</span><span class="p">,</span> <span class="no">SEL</span> <span class="n">sel</span><span class="p">,</span> <span class="nb">id</span> <span class="n">inst</span><span class="p">)</span> <span class="p">{</span>
	<span class="sr">//</span> <span class="err">用</span> <span class="n">isa</span> <span class="err">查看是否指向元类</span> <span class="no">Meta</span> <span class="no">Class</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span> <span class="n">cls</span><span class="o">-&gt;</span><span class="n">isMetaClass</span><span class="p">())</span> <span class="p">{</span>
        <span class="sr">//</span> <span class="n">try</span> <span class="o">[</span><span class="n">cls</span> <span class="ss">resolveInstanceMethod</span><span class="p">:</span><span class="n">sel</span><span class="o">]</span>
        <span class="n">_class_resolveInstanceMethod</span><span class="p">(</span><span class="n">cls</span><span class="p">,</span> <span class="n">sel</span><span class="p">,</span> <span class="n">inst</span><span class="p">);</span>
    <span class="p">}</span> 
    <span class="k">else</span> <span class="p">{</span>
        <span class="sr">//</span> <span class="n">try</span> <span class="o">[</span><span class="n">nonMetaClass</span> <span class="ss">resolveClassMethod</span><span class="p">:</span><span class="n">sel</span><span class="o">]</span>
        <span class="sr">//</span> <span class="ow">and</span> <span class="o">[</span><span class="n">cls</span> <span class="ss">resolveInstanceMethod</span><span class="p">:</span><span class="n">sel</span><span class="o">]</span>
        <span class="n">_class_resolveClassMethod</span><span class="p">(</span><span class="n">cls</span><span class="p">,</span> <span class="n">sel</span><span class="p">,</span> <span class="n">inst</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">lookUpImpOrNil</span><span class="p">(</span><span class="n">cls</span><span class="p">,</span> <span class="n">sel</span><span class="p">,</span> <span class="n">inst</span><span class="p">,</span> 
                            <span class="no">NO</span><span class="o">/*</span><span class="kp">initialize</span><span class="o">*/</span><span class="p">,</span> <span class="no">YES</span><span class="o">/*</span><span class="n">cache</span><span class="o">*/</span><span class="p">,</span> <span class="no">NO</span><span class="o">/*</span><span class="n">resolver</span><span class="o">*/</span><span class="p">))</span> 
        <span class="p">{</span>
            <span class="n">_class_resolveInstanceMethod</span><span class="p">(</span><span class="n">cls</span><span class="p">,</span> <span class="n">sel</span><span class="p">,</span> <span class="n">inst</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>此方法是动态方法解析的入口，会间接地发送 <code>+resolveInstanceMethod</code> 或 <code>+resolveClassMethod</code> 消息。通过对 isa 指向的判断，从而分辨出如果是对象方法，则进入 <code>+resolveInstanceMethod</code> 方法，如果是类方法，则进入 <code>+resolveClassMethod</code> 方法。</p>
<p>而上述代码中的 <code>_class_resolveInstanceMethod</code> 方法，我们从源码中看到是如此定义的：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">static</span> <span class="n">void</span> <span class="n">_class_resolveInstanceMethod</span><span class="p">(</span><span class="no">Class</span> <span class="n">cls</span><span class="p">,</span> <span class="no">SEL</span> <span class="n">sel</span><span class="p">,</span> <span class="nb">id</span> <span class="n">inst</span><span class="p">)</span> <span class="p">{</span>
	<span class="sr">//</span> <span class="err">首先查找是否有</span> <span class="n">resolveInstanceMethod</span> <span class="err">方法</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span> <span class="n">lookUpImpOrNil</span><span class="p">(</span><span class="n">cls</span><span class="o">-&gt;</span><span class="no">ISA</span><span class="p">(),</span> <span class="no">SEL_resolveInstanceMethod</span><span class="p">,</span> <span class="n">cls</span><span class="p">,</span> 
                         <span class="no">NO</span><span class="o">/*</span><span class="kp">initialize</span><span class="o">*/</span><span class="p">,</span> <span class="no">YES</span><span class="o">/*</span><span class="n">cache</span><span class="o">*/</span><span class="p">,</span> <span class="no">NO</span><span class="o">/*</span><span class="n">resolver</span><span class="o">*/</span><span class="p">))</span> 
    <span class="p">{</span>
        <span class="sr">//</span> <span class="no">Resolver</span> <span class="ow">not</span> <span class="n">implemented</span><span class="o">.</span>
        <span class="k">return</span><span class="p">;</span>
    <span class="p">}</span>
	<span class="sr">//</span> <span class="err">构造布尔类型变量表达式，动态绑定函数</span>
    <span class="no">BOOL</span> <span class="p">(</span><span class="o">*</span><span class="n">msg</span><span class="p">)(</span><span class="no">Class</span><span class="p">,</span> <span class="no">SEL</span><span class="p">,</span> <span class="no">SEL</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="n">__typeof__</span><span class="p">(</span><span class="n">msg</span><span class="p">))</span><span class="n">objc_msgSend</span><span class="p">;</span>
    <span class="sr">//</span> <span class="err">获得是否重新传递消息标记</span>
    <span class="n">bool</span> <span class="n">resolved</span> <span class="o">=</span> <span class="n">msg</span><span class="p">(</span><span class="n">cls</span><span class="p">,</span> <span class="no">SEL_resolveInstanceMethod</span><span class="p">,</span> <span class="n">sel</span><span class="p">);</span>
    <span class="sr">//</span> <span class="no">Cache</span> <span class="n">the</span> <span class="n">result</span> <span class="p">(</span><span class="n">good</span> <span class="ow">or</span> <span class="n">bad</span><span class="p">)</span> <span class="n">so</span> <span class="n">the</span> <span class="n">resolver</span> <span class="n">doesnt</span> <span class="n">fire</span> <span class="k">next</span> <span class="n">time</span><span class="o">.</span>
    <span class="sr">//</span> <span class="o">+</span><span class="n">resolveInstanceMethod</span> <span class="n">adds</span> <span class="n">to</span> <span class="nb">self</span> <span class="n">a</span><span class="o">.</span><span class="n">k</span><span class="o">.</span><span class="n">a</span><span class="o">.</span> <span class="n">cls</span>
    <span class="sr">//</span> <span class="err">调用</span> <span class="n">lookUpImpOrNil</span> <span class="err">并重新启动缓存，查看是否已经添加上了选择子对应的</span> <span class="no">IMP</span> <span class="err">指针</span>
    <span class="no">IMP</span> <span class="n">imp</span> <span class="o">=</span> <span class="n">lookUpImpOrNil</span><span class="p">(</span><span class="n">cls</span><span class="p">,</span> <span class="n">sel</span><span class="p">,</span> <span class="n">inst</span><span class="p">,</span> 
                             <span class="no">NO</span><span class="o">/*</span><span class="kp">initialize</span><span class="o">*/</span><span class="p">,</span> <span class="no">YES</span><span class="o">/*</span><span class="n">cache</span><span class="o">*/</span><span class="p">,</span> <span class="no">NO</span><span class="o">/*</span><span class="n">resolver</span><span class="o">*/</span><span class="p">);</span>
	<span class="sr">//</span> <span class="err">对查询到的</span> <span class="no">IMP</span> <span class="err">进行</span> <span class="n">log</span> <span class="err">输出</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">resolved</span>  <span class="o">&amp;&amp;</span>  <span class="no">PrintResolving</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">imp</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">_objc_inform</span><span class="p">(</span><span class="s2">&quot;RESOLVE: method %c[%s %s] &quot;</span>
                         <span class="s2">&quot;dynamically resolved to %p&quot;</span><span class="p">,</span> 
                         <span class="n">cls</span><span class="o">-&gt;</span><span class="n">isMetaClass</span><span class="p">()</span> <span class="p">?</span> <span class="s1">&#39;+&#39;</span> <span class="p">:</span> <span class="s1">&#39;-&#39;</span><span class="p">,</span> 
                         <span class="n">cls</span><span class="o">-&gt;</span><span class="n">nameForLogging</span><span class="p">(),</span> <span class="n">sel_getName</span><span class="p">(</span><span class="n">sel</span><span class="p">),</span> <span class="n">imp</span><span class="p">);</span>
        <span class="p">}</span>
        <span class="k">else</span> <span class="p">{</span>
            <span class="sr">//</span> <span class="no">Method</span> <span class="n">resolver</span> <span class="n">didnt</span> <span class="n">add</span> <span class="n">anything?</span>
            <span class="n">_objc_inform</span><span class="p">(</span><span class="s2">&quot;RESOLVE: +[%s resolveInstanceMethod:%s] returned YES&quot;</span>
                         <span class="s2">&quot;, but no new implementation of %c[%s %s] was found&quot;</span><span class="p">,</span>
                         <span class="n">cls</span><span class="o">-&gt;</span><span class="n">nameForLogging</span><span class="p">(),</span> <span class="n">sel_getName</span><span class="p">(</span><span class="n">sel</span><span class="p">),</span> 
                         <span class="n">cls</span><span class="o">-&gt;</span><span class="n">isMetaClass</span><span class="p">()</span> <span class="p">?</span> <span class="s1">&#39;+&#39;</span> <span class="p">:</span> <span class="s1">&#39;-&#39;</span><span class="p">,</span> 
                         <span class="n">cls</span><span class="o">-&gt;</span><span class="n">nameForLogging</span><span class="p">(),</span> <span class="n">sel_getName</span><span class="p">(</span><span class="n">sel</span><span class="p">));</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>通过 <code>_class_resolveInstanceMethod</code> 可以了解到，这只是通过 <code>+resolveInstanceMethod</code> 来查询是否开发者已经在运行时将其动态插入类中的实现函数。并且重新触发 <code>objc_msgSend</code> 方法。这里有一个 C 的语法值得我们去延伸学习一下，就是关于关键字 <code>__typeof__</code> 的。<code>__typeof__(var)</code> 是 GCC 对 C 的一个扩展保留字(<a href="https://gcc.gnu.org/onlinedocs/gcc/Typeof.html">官方文档</a>)，这里是用来描述一个指针的类型。</p>
<p>我们发现，最终都会返回到 <code>objc_msgSend</code> 中。反观一下上一篇文章写的 <code>objc_msgSend</code> 函数，是通过汇编语言实现的。在 <em><a href="https://www.mikeash.com/pyblog/friday-qa-2012-11-16-lets-build-objc_msgsend.html">Let’s build objc_msgsend</a></em> 这篇资料中，记录了一个关于 <code>objc_msgSend</code> 的伪代码。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="nb">id</span> <span class="n">objc_msgSend</span><span class="p">(</span><span class="nb">id</span> <span class="nb">self</span><span class="p">,</span> <span class="no">SEL</span> <span class="n">_cmd</span><span class="p">,</span> <span class="o">.</span><span class="n">.</span><span class="o">.</span><span class="p">)</span> <span class="p">{</span>
	<span class="no">Class</span> <span class="n">c</span> <span class="o">=</span> <span class="n">object_getClass</span><span class="p">(</span><span class="nb">self</span><span class="p">);</span>
	<span class="no">IMP</span> <span class="n">imp</span> <span class="o">=</span> <span class="n">cache_lookup</span><span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="n">_cmd</span><span class="p">);</span>
	<span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="n">imp</span><span class="p">)</span>
		<span class="n">imp</span> <span class="o">=</span> <span class="n">class_getMethodImplementation</span><span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="n">_cmd</span><span class="p">);</span>
	<span class="k">return</span> <span class="n">imp</span><span class="p">(</span><span class="nb">self</span><span class="p">,</span> <span class="n">_cmd</span><span class="p">,</span> <span class="o">.</span><span class="n">.</span><span class="o">.</span><span class="p">);</span>
<span class="p">}</span></code></pre></div>
<p>在缓存中无法直接击中 IMP 时，会调用 <code>class_getMethodImplementation</code> 方法。在 runtime 中，查看一下 <code>class_getMethodImplementation</code> 方法。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">IMP</span> <span class="n">class_getMethodImplementation</span><span class="p">(</span><span class="no">Class</span> <span class="n">cls</span><span class="p">,</span> <span class="no">SEL</span> <span class="n">sel</span><span class="p">)</span>
<span class="p">{</span>
    <span class="no">IMP</span> <span class="n">imp</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">cls</span>  <span class="o">||</span>  <span class="o">!</span><span class="n">sel</span><span class="p">)</span> <span class="k">return</span> <span class="kp">nil</span><span class="p">;</span>
	<span class="sr">//</span> <span class="err">上一篇文章的搜索入口</span>
    <span class="n">imp</span> <span class="o">=</span> <span class="n">lookUpImpOrNil</span><span class="p">(</span><span class="n">cls</span><span class="p">,</span> <span class="n">sel</span><span class="p">,</span> <span class="kp">nil</span><span class="p">,</span> 
                         <span class="no">YES</span><span class="o">/*</span><span class="kp">initialize</span><span class="o">*/</span><span class="p">,</span> <span class="no">YES</span><span class="o">/*</span><span class="n">cache</span><span class="o">*/</span><span class="p">,</span> <span class="no">YES</span><span class="o">/*</span><span class="n">resolver</span><span class="o">*/</span><span class="p">);</span>
    <span class="sr">//</span> <span class="no">Translate</span> <span class="n">forwarding</span> <span class="n">function</span> <span class="n">to</span> <span class="n">C</span><span class="o">-</span><span class="n">callable</span> <span class="n">external</span> <span class="n">version</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">imp</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">_objc_msgForward</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="n">imp</span><span class="p">;</span>
<span class="p">}</span></code></pre></div>
<p>在上一篇文中，详细介绍过了 <code>lookUpImpOrNil</code> 函数成功搜索的流程。而本例中与前相反，我们我发现该函数返回了一个 <code>_objc_msgForward</code> 的 IMP。此时，我们击中的函数是 <code>_objc_msgForward</code> 这个 IMP ，于是消息转发机制进入了<strong>备援接收</strong>流程。</p>
<h2 id="forwarding-">Forwarding 备援接收</h2>
<p><code>_objc_msgForward</code> 居然可以返回，说同 IMP 一样是一个指针。在 <code>objc-msg-x86_64.s</code> 中发现了其汇编实现。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">ENTRY</span>	<span class="n">__objc_msgForward</span>
<span class="sr">//</span> <span class="no">Non</span><span class="o">-</span><span class="n">stret</span> <span class="n">version</span>
<span class="sr">//</span> <span class="err">调用</span> <span class="n">__objc_forward_handler</span>
<span class="n">movq</span>	<span class="n">__objc_forward_handler</span><span class="p">(</span><span class="o">%</span><span class="n">rip</span><span class="p">),</span> <span class="o">%</span><span class="n">r11</span>
<span class="n">jmp</span>	<span class="o">*%</span><span class="n">r11</span>
<span class="no">END_ENTRY</span>	<span class="n">__objc_msgForward</span></code></pre></div>
<p>发现在接收到 <code>_objc_msgForward</code> 指针后，会立即进入 <code>__objc_forward_handler</code> 方法。其源码在 <code>objc-runtime.mm</code> 中。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1">#if !__OBJC2__</span>
<span class="sr">//</span> <span class="no">Default</span> <span class="n">forward</span> <span class="n">handler</span> <span class="p">(</span><span class="kp">nil</span><span class="p">)</span> <span class="n">goes</span> <span class="n">to</span> <span class="n">forward</span><span class="o">::</span> <span class="n">dispatch</span><span class="o">.</span>
<span class="n">void</span> <span class="o">*</span><span class="n">_objc_forward_handler</span> <span class="o">=</span> <span class="kp">nil</span><span class="p">;</span>
<span class="n">void</span> <span class="o">*</span><span class="n">_objc_forward_stret_handler</span> <span class="o">=</span> <span class="kp">nil</span><span class="p">;</span>
<span class="c1">#else</span>
<span class="sr">//</span> <span class="no">Default</span> <span class="n">forward</span> <span class="n">handler</span> <span class="n">halts</span> <span class="n">the</span> <span class="n">process</span><span class="o">.</span>
<span class="n">__attribute__</span><span class="p">((</span><span class="n">noreturn</span><span class="p">))</span> <span class="n">void</span> 
<span class="n">objc_defaultForwardHandler</span><span class="p">(</span><span class="nb">id</span> <span class="nb">self</span><span class="p">,</span> <span class="no">SEL</span> <span class="n">sel</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">_objc_fatal</span><span class="p">(</span><span class="s2">&quot;%c[%s %s]: unrecognized selector sent to instance %p &quot;</span>
                <span class="s2">&quot;(no message forward handler is installed)&quot;</span><span class="p">,</span> 
                <span class="n">class_isMetaClass</span><span class="p">(</span><span class="n">object_getClass</span><span class="p">(</span><span class="nb">self</span><span class="p">))</span> <span class="p">?</span> <span class="s1">&#39;+&#39;</span> <span class="p">:</span> <span class="s1">&#39;-&#39;</span><span class="p">,</span> 
                <span class="n">object_getClassName</span><span class="p">(</span><span class="nb">self</span><span class="p">),</span> <span class="n">sel_getName</span><span class="p">(</span><span class="n">sel</span><span class="p">),</span> <span class="nb">self</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">void</span> <span class="o">*</span><span class="n">_objc_forward_handler</span> <span class="o">=</span> <span class="p">(</span><span class="n">void</span><span class="o">*</span><span class="p">)</span><span class="n">objc_defaultForwardHandler</span><span class="p">;</span></code></pre></div>
<p>在 ObjC 2.0 以前，<code>_objc_forward_handler</code> 是 nil ，而在最新的 runtime 中，其实现由 <code>objc_defaultForwardHandler</code> 完成。其源码仅仅是在 log 中记录一些相关信息，这也是 handler 的主要功能。</p>
<p>而抛开 runtime ，看见了关键字 <code>__attribute__((noreturn))</code> 。这里简单介绍一下 GCC 中的又一扩展 <strong>__attribute__机制</strong> 。它用于与编译器直接交互，这是一个编译器指令(Compiler Directive)，用来在函数或数据声明中设置属性，从而进一步进行优化(继续了解可以阅读 <em><a href="http://nshipster.com/__attribute__/">NShipster __attribute__</a></em>)。而这里的 <code>__attribute__((noreturn))</code> 是告诉编译器此函数不会返回给调用者，以便编译器在优化时去掉不必要的函数返回代码。</p>
<p>Handler 的全部工作是记录日志、触发 crash 机制。如果开发者想实现消息转发，则需要重写 <code>_objc_forward_handler</code> 中的实现。这时引入 <code>objc_setForwardHandler</code> 方法：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">void</span> <span class="n">objc_setForwardHandler</span><span class="p">(</span><span class="n">void</span> <span class="o">*</span><span class="n">fwd</span><span class="p">,</span> <span class="n">void</span> <span class="o">*</span><span class="n">fwd_stret</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">_objc_forward_handler</span> <span class="o">=</span> <span class="n">fwd</span><span class="p">;</span>
<span class="c1">#if SUPPORT_STRET</span>
    <span class="n">_objc_forward_stret_handler</span> <span class="o">=</span> <span class="n">fwd_stret</span><span class="p">;</span>
<span class="c1">#endif</span>
<span class="p">}</span></code></pre></div>
<p>这是一个十分简单的动态绑定过程，让方法指针指向传入参数指针得以实现。</p>
<h2 id="core-foundation-">Core Foundation 衔接</h2>
<p>引入 <code>objc_setForwardHandler</code> 方法后，会有一个疑问：如何调用它？先来看一段异常信息：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="mi">2016</span><span class="o">-</span><span class="mi">08</span><span class="o">-</span><span class="mi">27</span> <span class="mi">08</span><span class="p">:</span><span class="mi">26</span><span class="p">:</span><span class="mi">08</span><span class="o">.</span><span class="mi">264</span> <span class="n">debug</span><span class="o">-</span><span class="n">objc</span><span class="o">[</span><span class="mi">7013</span><span class="p">:</span><span class="mi">29381250</span><span class="o">]</span> <span class="o">-[</span><span class="no">DGObject</span> <span class="n">test_no_exist</span><span class="o">]</span><span class="p">:</span> <span class="n">unrecognized</span> <span class="n">selector</span> <span class="n">sent</span> <span class="n">to</span> <span class="n">instance</span> <span class="mh">0x101200310</span>
<span class="mi">2016</span><span class="o">-</span><span class="mi">08</span><span class="o">-</span><span class="mi">27</span> <span class="mi">10</span><span class="p">:</span><span class="mi">09</span><span class="p">:</span><span class="mi">16</span><span class="o">.</span><span class="mi">495</span> <span class="n">debug</span><span class="o">-</span><span class="n">objc</span><span class="o">[</span><span class="mi">7013</span><span class="p">:</span><span class="mi">29381250</span><span class="o">]</span> <span class="o">***</span> <span class="no">Terminating</span> <span class="n">app</span> <span class="n">due</span> <span class="n">to</span> <span class="n">uncaught</span> <span class="n">exception</span> <span class="s1">&#39;NSInvalidArgumentException&#39;</span><span class="p">,</span> <span class="ss">reason</span><span class="p">:</span> <span class="s1">&#39;-[DGObject test_no_exist]: unrecognized selector sent to instance 0x101200310&#39;</span>
<span class="o">***</span> <span class="no">First</span> <span class="kp">throw</span> <span class="n">call</span> <span class="ss">stack</span><span class="p">:</span>
<span class="p">(</span>
	<span class="mi">0</span>   <span class="no">CoreFoundation</span>                      <span class="mh">0x00007fff842c64f2</span> <span class="n">__exceptionPreprocess</span> <span class="o">+</span> <span class="mi">178</span>
	<span class="mi">1</span>   <span class="n">libobjc</span><span class="o">.</span><span class="n">A</span><span class="o">.</span><span class="n">dylib</span>                     <span class="mh">0x000000010002989f</span> <span class="n">objc_exception_throw</span> <span class="o">+</span> <span class="mi">47</span>
	<span class="mi">2</span>   <span class="no">CoreFoundation</span>                      <span class="mh">0x00007fff843301ad</span> <span class="o">-[</span><span class="no">NSObject</span><span class="p">(</span><span class="no">NSObject</span><span class="p">)</span> <span class="ss">doesNotRecognizeSelector</span><span class="p">:</span><span class="o">]</span> <span class="o">+</span> <span class="mi">205</span>
	<span class="mi">3</span>   <span class="no">CoreFoundation</span>                      <span class="mh">0x00007fff84236571</span> <span class="n">___forwarding___</span> <span class="o">+</span> <span class="mi">1009</span>
	<span class="mi">4</span>   <span class="no">CoreFoundation</span>                      <span class="mh">0x00007fff842360f8</span> <span class="n">_CF_forwarding_prep_0</span> <span class="o">+</span> <span class="mi">120</span>
	<span class="mi">5</span>   <span class="n">debug</span><span class="o">-</span><span class="n">objc</span>                          <span class="mh">0x0000000100000e9e</span> <span class="n">main</span> <span class="o">+</span> <span class="mi">94</span>
	<span class="mi">6</span>   <span class="n">libdyld</span><span class="o">.</span><span class="n">dylib</span>                       <span class="mh">0x00007fff852a95ad</span> <span class="n">start</span> <span class="o">+</span> <span class="mi">1</span>
	<span class="mi">7</span>   <span class="o">?</span><span class="sc">??</span>                                 <span class="mh">0x0000000000000001</span> <span class="mh">0x0</span> <span class="o">+</span> <span class="mi">1</span>
<span class="p">)</span>
<span class="n">libc</span><span class="o">++</span><span class="n">abi</span><span class="o">.</span><span class="n">dylib</span><span class="p">:</span> <span class="n">terminating</span> <span class="n">with</span> <span class="n">uncaught</span> <span class="n">exception</span> <span class="n">of</span> <span class="n">type</span> <span class="no">NSException</span></code></pre></div>
<p>这个日志场景都接触过。从调用栈上，发现了最终是通过 Core Foundation 抛出异常。在 Core Foundation 的 <a href="https://github.com/opensource-apple/CF/blob/master/CFRuntime.c">CFRuntime.c</a> 无法找到 <code>objc_setForwardHandler</code> 方法的调用入口。综合参看 <em><a href="http://yulingtianxia.com/blog/2016/06/15/Objective-C-Message-Sending-and-Forwarding/">Objective-C 消息发送与转发机制原理</a></em> 和 <em><a href="http://arigrant.com/blog/2013/12/13/a-selector-left-unhandled">Hmmm, What’s that Selector?</a></em> 两篇文章，我们发现了在 <a href="https://github.com/opensource-apple/CF/blob/master/CFRuntime.c">CFRuntime.c</a> 的 <code>__CFInitialize()</code> 方法中，实际上是调用了 <code>objc_setForwardHandler</code> ，这段代码被苹果公司隐藏。</p>
<p>在上述调用栈中，发现了在 Core Foundation 中会调用 <code>___forwarding___</code> 。根据资料也可以了解到，在 <code>objc_setForwardHandler</code> 时会传入 <code>__CF_forwarding_prep_0</code> 和 <code>___forwarding_prep_1___</code> 两个参数，而这两个指针都会调用 <code>____forwarding___</code> 。这个函数中，也交代了消息转发的逻辑。在 <em><a href="http://arigrant.com/blog/2013/12/13/a-selector-left-unhandled">Hmmm, What’s that Selector?</a></em> 文章中，复原了 <code>____forwarding___</code> 的实现。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="sr">//</span> <span class="err">两个参数：前者为被转发消息的栈指针</span> <span class="no">IMP</span> <span class="err">，后者为是否返回结构体</span>
<span class="n">int</span> <span class="n">__forwarding__</span><span class="p">(</span><span class="n">void</span> <span class="o">*</span><span class="n">frameStackPointer</span><span class="p">,</span> <span class="n">int</span> <span class="n">isStret</span><span class="p">)</span> <span class="p">{</span>
  <span class="nb">id</span> <span class="n">receiver</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="nb">id</span> <span class="o">*</span><span class="p">)</span><span class="n">frameStackPointer</span><span class="p">;</span>
  <span class="no">SEL</span> <span class="n">sel</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="no">SEL</span> <span class="o">*</span><span class="p">)(</span><span class="n">frameStackPointer</span> <span class="o">+</span> <span class="mi">8</span><span class="p">);</span>
  <span class="n">const</span> <span class="n">char</span> <span class="o">*</span><span class="n">selName</span> <span class="o">=</span> <span class="n">sel_getName</span><span class="p">(</span><span class="n">sel</span><span class="p">);</span>
  <span class="no">Class</span> <span class="n">receiverClass</span> <span class="o">=</span> <span class="n">object_getClass</span><span class="p">(</span><span class="n">receiver</span><span class="p">);</span>
  <span class="sr">//</span> <span class="err">调用</span> <span class="ss">forwardingTargetForSelector</span><span class="p">:</span>
  <span class="sr">//</span> <span class="err">进入</span> <span class="err">备援接收</span> <span class="err">主要步骤</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">class_respondsToSelector</span><span class="p">(</span><span class="n">receiverClass</span><span class="p">,</span> <span class="vi">@selector</span><span class="p">(</span><span class="ss">forwardingTargetForSelector</span><span class="p">:)))</span> <span class="p">{</span>
	<span class="sr">//</span> <span class="err">获得方法签名</span>
    <span class="nb">id</span> <span class="n">forwardingTarget</span> <span class="o">=</span> <span class="o">[</span><span class="n">receiver</span> <span class="ss">forwardingTargetForSelector</span><span class="p">:</span><span class="n">sel</span><span class="o">]</span><span class="p">;</span>
    <span class="sr">//</span> <span class="err">判断返回类型是否正确</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">forwardingTarget</span> <span class="o">&amp;&amp;</span> <span class="n">forwarding</span> <span class="o">!=</span> <span class="n">receiver</span><span class="p">)</span> <span class="p">{</span>
	    <span class="sr">//</span> <span class="err">判断类型，是否返回值为结构体，选用不同的转发方法</span>
    	<span class="k">if</span> <span class="p">(</span><span class="n">isStret</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
    		<span class="n">int</span> <span class="n">ret</span><span class="p">;</span>
    		<span class="n">objc_msgSend_stret</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ret</span><span class="p">,</span><span class="n">forwardingTarget</span><span class="p">,</span> <span class="n">sel</span><span class="p">,</span> <span class="o">.</span><span class="n">.</span><span class="o">.</span><span class="p">);</span>
    		<span class="k">return</span> <span class="n">ret</span><span class="p">;</span>
    	<span class="p">}</span>
      <span class="k">return</span> <span class="n">objc_msgSend</span><span class="p">(</span><span class="n">forwardingTarget</span><span class="p">,</span> <span class="n">sel</span><span class="p">,</span> <span class="o">.</span><span class="n">.</span><span class="o">.</span><span class="p">);</span>
    <span class="p">}</span>
  <span class="p">}</span>
  <span class="sr">//</span> <span class="err">僵尸对象</span>
  <span class="n">const</span> <span class="n">char</span> <span class="o">*</span><span class="n">className</span> <span class="o">=</span> <span class="n">class_getName</span><span class="p">(</span><span class="n">receiverClass</span><span class="p">);</span>
  <span class="n">const</span> <span class="n">char</span> <span class="o">*</span><span class="n">zombiePrefix</span> <span class="o">=</span> <span class="s2">&quot;_NSZombie_&quot;</span><span class="p">;</span>
  <span class="n">size_t</span> <span class="n">prefixLen</span> <span class="o">=</span> <span class="n">strlen</span><span class="p">(</span><span class="n">zombiePrefix</span><span class="p">);</span> <span class="sr">//</span> <span class="mh">0xa</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">strncmp</span><span class="p">(</span><span class="n">className</span><span class="p">,</span> <span class="n">zombiePrefix</span><span class="p">,</span> <span class="n">prefixLen</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
    <span class="no">CFLog</span><span class="p">(</span><span class="n">kCFLogLevelError</span><span class="p">,</span>
          <span class="err">@</span><span class="s2">&quot;*** -[%s %s]: message sent to deallocated instance %p&quot;</span><span class="p">,</span>
          <span class="n">className</span> <span class="o">+</span> <span class="n">prefixLen</span><span class="p">,</span>
          <span class="n">selName</span><span class="p">,</span>
          <span class="n">receiver</span><span class="p">);</span>
    <span class="o">&lt;</span><span class="n">breakpoint</span><span class="o">-</span><span class="n">interrupt</span><span class="o">&gt;</span>
  <span class="p">}</span>
  <span class="sr">//</span> <span class="err">调用</span> <span class="n">methodSignatureForSelector</span> <span class="err">获取方法签名后再调用</span> <span class="n">forwardInvocation</span>
  <span class="sr">//</span> <span class="err">进入消息转发系统</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">class_respondsToSelector</span><span class="p">(</span><span class="n">receiverClass</span><span class="p">,</span> <span class="vi">@selector</span><span class="p">(</span><span class="ss">methodSignatureForSelector</span><span class="p">:)))</span> <span class="p">{</span>
    <span class="no">NSMethodSignature</span> <span class="o">*</span><span class="n">methodSignature</span> <span class="o">=</span> <span class="o">[</span><span class="n">receiver</span> <span class="ss">methodSignatureForSelector</span><span class="p">:</span><span class="n">sel</span><span class="o">]</span><span class="p">;</span>
    <span class="sr">//</span> <span class="err">判断返回类型是否正确</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">methodSignature</span><span class="p">)</span> <span class="p">{</span>
      <span class="no">BOOL</span> <span class="n">signatureIsStret</span> <span class="o">=</span> <span class="o">[</span><span class="n">methodSignature</span> <span class="n">_frameDescriptor</span><span class="o">]-&gt;</span><span class="n">returnArgInfo</span><span class="o">.</span><span class="n">flags</span><span class="o">.</span><span class="n">isStruct</span><span class="p">;</span>
      <span class="k">if</span> <span class="p">(</span><span class="n">signatureIsStret</span> <span class="o">!=</span> <span class="n">isStret</span><span class="p">)</span> <span class="p">{</span>
        <span class="no">CFLog</span><span class="p">(</span><span class="n">kCFLogLevelWarning</span> <span class="p">,</span>
              <span class="err">@</span><span class="s2">&quot;*** NSForwarding: warning: method signature and compiler disagree on struct-return-edness of &#39;%s&#39;.  Signature thinks it does%s return a struct, and compiler thinks it does%s.&quot;</span><span class="p">,</span>
              <span class="n">selName</span><span class="p">,</span>
              <span class="n">signatureIsStret</span> <span class="p">?</span> <span class="s2">&quot;&quot;</span> <span class="p">:</span> <span class="ow">not</span><span class="p">,</span>
              <span class="n">isStret</span> <span class="p">?</span> <span class="s2">&quot;&quot;</span> <span class="p">:</span> <span class="ow">not</span><span class="p">);</span>
      <span class="p">}</span>
      <span class="k">if</span> <span class="p">(</span><span class="n">class_respondsToSelector</span><span class="p">(</span><span class="n">receiverClass</span><span class="p">,</span> <span class="vi">@selector</span><span class="p">(</span><span class="ss">forwardInvocation</span><span class="p">:)))</span> <span class="p">{</span>
	    <span class="sr">//</span> <span class="err">传入消息的全部细节信息</span>
        <span class="no">NSInvocation</span> <span class="o">*</span><span class="n">invocation</span> <span class="o">=</span> <span class="o">[</span><span class="no">NSInvocation</span> <span class="ss">_invocationWithMethodSignature</span><span class="p">:</span><span class="n">methodSignature</span> <span class="ss">frame</span><span class="p">:</span><span class="n">frameStackPointer</span><span class="o">]</span><span class="p">;</span>
        <span class="o">[</span><span class="n">receiver</span> <span class="ss">forwardInvocation</span><span class="p">:</span><span class="n">invocation</span><span class="o">]</span><span class="p">;</span>
        <span class="n">void</span> <span class="o">*</span><span class="n">returnValue</span> <span class="o">=</span> <span class="no">NULL</span><span class="p">;</span>
        <span class="o">[</span><span class="n">invocation</span> <span class="ss">getReturnValue</span><span class="p">:</span><span class="o">&amp;</span><span class="n">value</span><span class="o">]</span><span class="p">;</span>
        <span class="k">return</span> <span class="n">returnValue</span><span class="p">;</span>
      <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="no">CFLog</span><span class="p">(</span><span class="n">kCFLogLevelWarning</span> <span class="p">,</span>
              <span class="err">@</span><span class="s2">&quot;*** NSForwarding: warning: object %p of class &#39;%s&#39; does not implement forwardInvocation: -- dropping message&quot;</span><span class="p">,</span>
              <span class="n">receiver</span><span class="p">,</span>
              <span class="n">className</span><span class="p">);</span>
        <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
      <span class="p">}</span>
    <span class="p">}</span>
  <span class="p">}</span>
  <span class="no">SEL</span> <span class="o">*</span><span class="n">registeredSel</span> <span class="o">=</span> <span class="n">sel_getUid</span><span class="p">(</span><span class="n">selName</span><span class="p">);</span>
  <span class="sr">//</span> <span class="n">selector</span> <span class="err">是否已经在</span> <span class="no">Runtime</span> <span class="err">注册过</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">sel</span> <span class="o">!=</span> <span class="n">registeredSel</span><span class="p">)</span> <span class="p">{</span>
    <span class="no">CFLog</span><span class="p">(</span><span class="n">kCFLogLevelWarning</span> <span class="p">,</span>
          <span class="err">@</span><span class="s2">&quot;*** NSForwarding: warning: selector (%p) for message &#39;%s&#39; does not match selector known to Objective C runtime (%p)-- abort&quot;</span><span class="p">,</span>
          <span class="n">sel</span><span class="p">,</span>
          <span class="n">selName</span><span class="p">,</span>
          <span class="n">registeredSel</span><span class="p">);</span>
  <span class="p">}</span> 
  <span class="sr">//</span> <span class="n">doesNotRecognizeSelector</span><span class="err">，主动抛出异常</span>
  <span class="sr">//</span> <span class="err">也就是前文我们看到的</span>
  <span class="sr">//</span> <span class="err">表明选择子未能得到处理</span>
  <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">class_respondsToSelector</span><span class="p">(</span><span class="n">receiverClass</span><span class="p">,</span><span class="vi">@selector</span><span class="p">(</span><span class="ss">doesNotRecognizeSelector</span><span class="p">:)))</span> <span class="p">{</span>
    <span class="o">[</span><span class="n">receiver</span> <span class="ss">doesNotRecognizeSelector</span><span class="p">:</span><span class="n">sel</span><span class="o">]</span><span class="p">;</span>
  <span class="p">}</span> 
  <span class="k">else</span> <span class="p">{</span>
    <span class="no">CFLog</span><span class="p">(</span><span class="n">kCFLogLevelWarning</span> <span class="p">,</span>
          <span class="err">@</span><span class="s2">&quot;*** NSForwarding: warning: object %p of class &#39;%s&#39; does not implement doesNotRecognizeSelector: -- abort&quot;</span><span class="p">,</span>
          <span class="n">receiver</span><span class="p">,</span>
          <span class="n">className</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="sr">//</span> <span class="no">The</span> <span class="n">point</span> <span class="n">of</span> <span class="n">no</span> <span class="k">return</span><span class="o">.</span>
  <span class="n">kill</span><span class="p">(</span><span class="n">getpid</span><span class="p">(),</span> <span class="mi">9</span><span class="p">);</span>
<span class="p">}</span></code></pre></div>
<h2 id="message-dispatch-system-">Message-Dispatch System 消息派发系统</h2>
<p>在大概了解过 Message-Dispatch System 的源码后，来简单的说明一下。由于在前两步中，我们无法找到那条消息的实现。则创建一个 NSInvocation 对象，并将消息全部属性记录下来。 NSInvocation 对象包括了选择子、target 以及其他参数。</p>
<p>随后，调用 <code>forwardInvocation:(NSInvocation *)invocation</code> 方法，其中的实现仅仅是改变了 target 指向，使消息保证能够调用。倘若发现本类无法处理，则继续想父类进行查找。直至 NSObject ，如果找到根类仍旧无法找到，则会调用 <code>doesNotRecognizeSelector:</code> ，以抛出异常。此异常表明选择子最终未能得到处理。</p>
<p>而对于 <code>doesNotRecognizeSelector:</code> 内部是如何实现，如何捕获异常。或者说 override 改方法后做自定义处理，等笔者实践后继续记录学习笔记。</p>
<h2 id="section-1">对于消息转发的总结梳理</h2>
<p>在 Core Foundation 的消息派发流程中，由于源码被隐藏，所以笔者无法亲自测试代码。倘若以后学习了逆向，可以再去探讨一下这里面发生的过程。</p>
<p>对于这篇文章记录的消息转发流程，大致如下图所示：</p>
<p><img src="http://7xwh85.com1.z0.glb.clouddn.com/Desktop.png" alt="Desktop" /></p>
<p>以上是对于 objc_msgSend 消息转发的源码学习笔记，请多指正。</p>
<hr />
<h2 id="section-2">参考资料</h2>
<p><a href="https://www.mikeash.com/pyblog/friday-qa-2012-11-16-lets-build-objc_msgsend.html">Let’s Build objc_msgSend</a></p>
<p><a href="http://arigrant.com/blog/2013/12/13/a-selector-left-unhandled">Hmmm, What’s that Selector?</a></p>
<p><a href="http://nshipster.com/__attribute__/">NShipster __attribute__</a></p>
<p><a href="http://yulingtianxia.com/blog/2016/06/15/Objective-C-Message-Sending-and-Forwarding/">Objective-C 消息发送与转发机制原理</a></p>
<blockquote>
  <p>若想查看更多的iOS Source Probe文章，收录在这个<a href="https://github.com/Desgard/iOS-Source-Probe">Github仓库中</a>。</p>
</blockquote>
  <footer class="post-footer">
    <section class="author">
      <h4>Desgard_Duan</h4>
      <p>I write many code to write less code.💻</p>
    </section>
<aside class="share">
  <h4>Share this.</h4>
  <a href="http://twitter.com/share?text=《objc_msgSend消息传递学习笔记 - 消息转发》 -- Guardia · 瓜地&amp;url=https://www.desgard.com/objc_msgSend2/"
  onclick="window.open(this.href, 'twitter-share', 'width=550,height=235');return false;">
    <i class="fa fa-twitter-square"></i>
  </a>
  <a href="http://v.t.sina.com.cn/share/share.php?url=https://www.desgard.com/objc_msgSend2/&amp;title=《objc_msgSend消息传递学习笔记 - 消息转发》 —— Guardia · 瓜地" onclick="window.open(this.href, 'twitter-share', 'width=550,height=235');return false;">
    <i class="fa fa-weibo"></i>
  </a>
</aside>
      <hr>
<aside id="comments" class="disqus">
  <div id="container"></div>
  <!-- <link rel="stylesheet" href="/assets/css/gitment.css">
  <script src="https://imsun.github.io/gitment/dist/gitment.browser.js"></script>
  <script>
  var gitment = new Gitment({
    id: "https://www.desgard.com/objc_msgSend2/", 
    owner: 'Desgard',
    repo: 'desgard.github.com',
    oauth: {
      client_id: 'e2612df42f3f2a83e71c',
      client_secret: 'b53e85b314bb24a6d06773e48bbb62a4de3b8b3a',
    },
  })
  gitment.render('container')
  </script> -->
<link rel="stylesheet" href="/assets/css/gitalk.css">
<script src="https://unpkg.com/gitalk/dist/gitalk.min.js"></script>
<div id='gitalk-container'></div>
<script>
    const gitalk = new Gitalk({
        id: "https://www.desgard.com/objc_msgSend2/", 
        clientID: 'e2612df42f3f2a83e71c',
        clientSecret: 'b53e85b314bb24a6d06773e48bbb62a4de3b8b3a',
        repo: 'desgard.github.com',
        owner: 'Desgard',
        admin: ['Desgard'],
        // facebook-like distraction free mode
        distractionFreeMode: false
    })
    gitalk.render('gitalk-container')
</script>
</aside>
  </footer>
</article>
    </main>
<footer class="footer">
  <div class="container">
    <ul class="icons">
      <li>
        <a href="https://github.com/desgard" class="icon-github" target="_blank">
          <i class="fa fa-github"></i>
        </a>
      </li>
      <li>
        <a href="https://www.facebook.com/desgard.duan" class="icon-facebook" target="_blank">
          <i class="fa fa-facebook"></i>
        </a>
      </li>
      <li>
        <a href="https://twitter.com/Desgard_Duan" class="icon-twitter" target="_blank">
          <i class="fa fa-twitter"></i>
        </a>
      </li>
      <li>
        <a href="https://stackoverflow.com/users/6119149/desgard-duan" class="icon-github" target="_blank">
          <i class="fa fa-stack-overflow"></i>
        </a>
      </li>
      <li>
        <a href="https://weibo.com/desgard" class="icon-instagram" target="_blank">
          <i class="fa fa-weibo"></i>
        </a>
      </li>
    </ul>
    <p>
      <q>I write many code to write less code.💻</q>
      <small>– Gua</small>
    </p>
    <small class="clearfix">
      Powered by <a href="http://jekyllrb.com" target="_blank">Jekyll</a> • <a href="https://github.com/desgard" target="_blank">Open source <i class="fa fa-heart"></i></a>
    </small>
  </div>
</footer>
<a class="scroll-up fa fa-chevron-up bounce" href="#" data-position="0"></a>
<div id="modalSearch" class="modal">
  <div class="modal__overlay"></div>
  <div class="modal__content">
    <a href="#!" class="modal-close" data-modal-close>&times;</a>
    <div class="search-container">
      <input type="text" id="search-input" placeholder="Search articles">
      <ul id="results-container"></ul>
    </div>
  </div>
</div>
    <script src="/assets/main-52d417e8a6ff9f5b168386d37c96338a.js"></script>
  </body>
  <script>
    var link = "" ;
    var os = function() {  
      var ua = navigator.userAgent,  
      isWindowsPhone = /(?:Windows Phone)/.test(ua),  
      isSymbian = /(?:SymbianOS)/.test(ua) || isWindowsPhone,   
      isAndroid = /(?:Android)/.test(ua),   
      isFireFox = /(?:Firefox)/.test(ua),   
      isChrome = /(?:Chrome|CriOS)/.test(ua),  
      isTablet = /(?:iPad|PlayBook)/.test(ua) || (isAndroid && !/(?:Mobile)/.test(ua)) || (isFireFox && /(?:Tablet)/.test(ua)),  
      isPhone = /(?:iPhone)/.test(ua) && !isTablet,  
      isPc = !isPhone && !isAndroid && !isSymbian;  
      return {  
        isTablet: isTablet,  
        isPhone: isPhone,  
        isAndroid : isAndroid,  
        isPc : isPc  
      };  
    }();  
    if (link.length > 0) {
      if (os.isAndroid || os.isPhone) {
        location.replace(link);
      }
    }
  </script>
</html>